#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/net.h>
#include <net/sock.h>
#include <asm/processor.h>
#include <asm/uaccess.h>
#include <linux/in.h>

#include <net/ksocket.h>

//******************header************
char index_page[50]="index.html";
char supportTypes[20][20]={".htm", "text/html",".html", "text/html",".txt", "text/plain",".jpeg" ,"image/jpeg",
				".jpg", "image/jpeg",".png", "image/png",".gif", "image/gif",".bmp", "image/bmp"};
char file_root[20]="";


struct rentry{
        unsigned int status;
        char* path;
        char* arg;
    };

//************************************
static int port = 4444;
module_param(port, int, 0444);

long int SIZE=4096*1024;

char recv_buffer[4096];

char method[50];
char path[200];
char version[50];

char status[3];
char dsrp[100];
char type[20];

char file_path[200];
char* space="\r\n";


//extern void ReadFile(char *Filename, int StartPos,char* output, int*length);
void analyze();
void determinStatus();
void BindingPage();
int IsTypeSupported(char* type);

void analyze()
{
    char* p=recv_buffer;
    struct rentry r;

    int i;

    //Copy method
    for(i=0;*p!='\r';i++){
        if(*(p+i)==' ')
        {
            strncpy(method,p,i);
            p=p+i+1;
            break;
        }
    }

    //Copy path
    for(i=0;*p!='\r';i++){
        if(*(p+i)==' ')
        {
            strncpy(path,p,i);
            p=p+i+1;
            break;
        }
    }

    //Copy verion
    for(i=0;;i++){
        if(*(p+i)=='\r')
        {
            strncpy(version,p,i);
            p+=i;
            break;
        }
    }

    //Type
    if (!strcmp(path,"/")){
        //Use the default page
        strcat(path,index_page);
        strcpy(type,".html");
        return;
    }

        char *p_type=strrchr(path,'.');
        if (!p_type)
        {
            strcpy(status,"401");
            strcpy(dsrp,"Bad Request: Invalid URI:");
            strcat(dsrp,path);
            return;

        }
        else{
            strcpy(type,p_type);

        }

    

}

void determinStatus()
{
    if (strcmp(method,"GET"))
    {
        strcpy(status,"401");
        strcpy(dsrp,"Bad Request: Invalid Method:");
        strcat(dsrp,method);

        return;
    }
    else if (strcmp(version,"HTTP/1.0") && strcmp(version,"HTTP/1.1"))
    {
        strcpy(status,"401");
        strcpy(dsrp,"Bad Request: Invalid HTTP-Version:");
        strcat(dsrp,version);

        return;
    }
    else if (strpbrk(path," <>#%"))
    {
        strcpy(status,"401");
        strcpy(dsrp,"Bad Request: Invalid URI:");
        strcat(dsrp,path);

        return;
    }
    if(!IsTypeSupported(type))
    {
        strcpy(status,"501");
        strcpy(dsrp," Not Implemented:");
        strcat(dsrp,path);
        return;
    }

    //Determine 404
    char* p_path=path;
    p_path++;
    char temp[50];
    strcpy(temp,file_root);
    strcat(temp,p_path);

    struct file 	*filp;
    filp = filp_open(temp,00,O_RDONLY);
    if (IS_ERR(filp)||(filp==NULL) || (filp->f_op->read==NULL) )
    {
	strcpy(status,"404");
        strcpy(dsrp,"Not Found:");
        strcpy(dsrp,path);
	return;
    }

    strcat(file_path,p_path);
    filp_close(filp,0);

    //Now is the 200
    strcpy(status,"200");
    strcpy(dsrp,"OK");
    return;
    
}

void BindingPage()
{
    if (strcmp(status,"200"))
    {
        strcpy(type,"text/html");
        strcat(file_path,status);
        strcat(file_path,".html");
    }
}

int IsTypeSupported(char* type)
{
    //convert the type html to text/html
    int i=0;
    for (i=0;i<20;i++){
        if (!strcmp(supportTypes[i],type))
        {
            strcpy(type,supportTypes[i+1]);
            return 1;
        }
    }
    return 0;
}


void ReadFile(char *Filename, int StartPos, char* output, long int* length)
{
	struct file 	*filp;
	char		*Buffer;
	mm_segment_t	oldfs;
	int		BytesRead;



	if (output==NULL) 
		return;
	
	filp = filp_open(Filename,00,O_RDONLY);
	if (IS_ERR(filp)||(filp==NULL))
		return;  /* Or do something else */

	if (filp->f_op->read==NULL)
		return;  /* File(system) doesn't allow reads */

	/* Now read 4096 bytes from postion "StartPos" */
	filp->f_pos = StartPos;
	oldfs = get_fs();
	set_fs(KERNEL_DS);
	BytesRead = filp->f_op->read(filp,output,SIZE,&filp->f_pos);
	printk("BytesRead: %d\n",BytesRead);
	
	
	set_fs(oldfs);

	/* Close the file */
	//fput(filp);
	filp_close(filp,0);

	*length=BytesRead;

	return ;

}



int tcp_srv(void *arg)
{
	ksocket_t sockfd_srv, sockfd_cli;
	struct sockaddr_in addr_srv;
	struct sockaddr_in addr_cli;
	char buf[4096], *tmp;
	int addr_len, len;

	char* output;
	long int  length=0;


	char header[50];
	char contentType[50];
	char contentLength[50];
	char str_length[20];	

	memset(str_length,0,sizeof(str_length));
	
	

#ifdef KSOCKET_ADDR_SAFE
		mm_segment_t old_fs;
		old_fs = get_fs();
		set_fs(KERNEL_DS);
#endif

	sprintf(current->comm, "ksktsrv"); /* kernel thread name*/
//	lock_kernel();	 /* This seems to be required for exit_mm */
//	exit_mm(current);
//	/* close open files too (stdin/out/err are open) */
//	exit_files(current);	
	
	sockfd_srv = sockfd_cli = NULL;
	memset(&addr_cli, 0, sizeof(addr_cli));
	memset(&addr_srv, 0, sizeof(addr_srv));
	addr_srv.sin_family = AF_INET;
	addr_srv.sin_port = htons(port);
	addr_srv.sin_addr.s_addr = INADDR_ANY;
	addr_len = sizeof(struct sockaddr_in);
	
	sockfd_srv = ksocket(AF_INET, SOCK_STREAM, 0);
	printk("sockfd_srv = 0x%p\n", sockfd_srv);
	if (sockfd_srv == NULL)
	{
		printk("socket failed\n");
		return -1;
	}
	if (kbind(sockfd_srv, (struct sockaddr *)&addr_srv, addr_len) < 0)
	{
		printk("bind failed\n");
		return -1;
	}

	if (klisten(sockfd_srv, 10) < 0)
	{
		printk("listen failed\n");
		return -1;
	}

	sockfd_cli = kaccept(sockfd_srv, (struct sockaddr *)&addr_cli, &addr_len);
	if (sockfd_cli == NULL)
	{
		printk("accept failed\n");
		return -1;
	}
	else
		printk("sockfd_cli = 0x%p\n", sockfd_cli);
	
	tmp = inet_ntoa(&addr_cli.sin_addr);
	printk("got connected from : %s %d\n", tmp, ntohs(addr_cli.sin_port));
	kfree(tmp);
	
	
	memset(recv_buffer, 0, sizeof(recv_buffer));
	len = krecv(sockfd_cli, recv_buffer, sizeof(recv_buffer), 0);
	if (len > 0)
	{
		printk("got message : %s\n", recv_buffer);
		output = kmalloc(SIZE,GFP_KERNEL);
		memset(output,0,SIZE);

	        analyze();
	        determinStatus();
	        BindingPage();

		//printk("%s %s %s\n",status,dsrp,type);

		
       		strcpy(header,"HTTP/1.0 ");
        	strcat(header,status);
   	    	strcat(header," ");
    	   	strcat(header,dsrp);
   	 	strcat(header,space);

		strcpy(contentType,"Content-Type: ");
	        strcat(contentType,type);
	        strcat(contentType,space);
        				
		length=0;
		ReadFile(file_path,0,output,&length);

		sprintf(str_length,"%ld",length);


	        strcpy(contentLength,"Content-Length: ");
	        strcat(contentLength,str_length);
	        strcat(contentLength,space);

		printk("%s\n",header);
		printk("%s\n",contentType);
		printk("%s\n",contentLength);


		ksend(sockfd_cli,header,strlen(header),0);
		ksend(sockfd_cli,contentType,strlen(contentType),0);
		ksend(sockfd_cli,contentLength,strlen(contentLength),0);
		ksend(sockfd_cli,"\r\n",strlen("\r\n"),0);
		ksend(sockfd_cli, output, length, 0);


	}

	kclose(sockfd_cli);
	kclose(sockfd_srv);
#ifdef KSOCKET_ADDR_SAFE
		set_fs(old_fs);
#endif
	
	return 0;
}

static int ksocket_tcp_srv_init(void)
{
	kernel_thread(tcp_srv, NULL, 0);
	
	printk("ksocket tcp srv init ok\n");
	return 0;
}

static void ksocket_tcp_srv_exit(void)
{
	printk("ksocket tcp srv exit\n");
}

module_init(ksocket_tcp_srv_init);
module_exit(ksocket_tcp_srv_exit);

MODULE_LICENSE("Dual BSD/GPL");
